/**
 * 字符串工具类
 * 提供字符串处理的各种实用方法
 */
export default class StringUtil {
  /** 空字符串 */
  private static readonly EMPTY = "";

  /** 下划线 */
  private static readonly SEPARATOR = '_';

  /** 星号 */
  private static readonly ASTERISK = '*';

  /**
   * 判断一个字符串是否为空串
   * 
   * @param str 字符串
   * @returns true：为空 false：非空
   */
  public static isEmpty(str: string | null | undefined): boolean {
    return str === null || str === undefined || str.trim() === this.EMPTY;
  }

  /**
   * 判断一个字符串是否为非空串
   * 
   * @param str 字符串
   * @returns true：非空串 false：空串
   */
  public static isNotEmpty(str: string | null | undefined): boolean {
    return !this.isEmpty(str);
  }

  /**
   * 判断字符串是否包含空白字符
   * 
   * @param str 字符串
   * @returns true：空白 false：非空白
   */
  public static isBlank(str: string | null | undefined): boolean {
    if (str === null || str === undefined) {
      return true;
    }
    return /^\s*$/.test(str);
  }

  /**
   * 判断字符串是否不包含空白字符
   * 
   * @param str 字符串
   * @returns true：非空白 false：空白
   */
  public static isNotBlank(str: string | null | undefined): boolean {
    return !this.isBlank(str);
  }

  /**
   * 安全地获取字符串值，null或undefined时返回默认值
   * 
   * @param value 要判断的value
   * @param defaultValue 默认值
   * @returns 返回值
   */
  public static defaultIfEmpty(value: string | null | undefined, defaultValue: string): string {
    return this.isEmpty(value) ? defaultValue : value!;
  }

  /**
   * 截取字符串
   * 
   * @param str 字符串
   * @param start 开始位置
   * @param end 结束位置
   * @returns 截取后的字符串
   */
  public static substring(str: string | null | undefined, start: number, end?: number): string {
    if (this.isEmpty(str)) {
      return this.EMPTY;
    }

    str = str!;
    if (end === undefined) {
      return str.substring(start);
    }

    if (end < 0) {
      end = str.length + end;
    }
    if (start < 0) {
      start = str.length + start;
    }

    if (end > str.length) {
      end = str.length;
    }

    if (start > end) {
      return this.EMPTY;
    }

    return str.substring(Math.max(start, 0), Math.max(end, 0));
  }

  /**
   * 在字符串中查找第一个出现的指定子串之间的内容
   * 
   * @param str 源字符串
   * @param open 开始字符串
   * @param close 结束字符串
   * @returns 截取的字符串，如果未找到则返回空字符串
   */
  public static substringBetween(str: string | null | undefined, open: string, close: string): string {
    if (str === null || str === undefined || open === null || close === null) {
      return this.EMPTY;
    }

    const start = str.indexOf(open);
    if (start !== -1) {
      const end = str.indexOf(close, start + open.length);
      if (end !== -1) {
        return str.substring(start + open.length, end);
      }
    }
    return this.EMPTY;
  }

  /**
   * 格式化字符串
   * 
   * @param template 模板字符串，使用{0}、{1}等作为占位符
   * @param args 要替换的参数
   * @returns 格式化后的字符串
   */
  public static format(template: string, ...args: any[]): string {
    if (this.isEmpty(template)) {
      return this.EMPTY;
    }
    
    return template.replace(/\{(\d+)\}/g, (match, index) => {
      const i = parseInt(index, 10);
      return i >= 0 && i < args.length ? String(args[i]) : match;
    });
  }

  /**
   * 转换为驼峰命名
   * 
   * @param str 下划线分隔的字符串
   * @returns 驼峰命名字符串
   */
  public static toCamelCase(str: string | null | undefined): string {
    if (this.isEmpty(str)) {
      return this.EMPTY;
    }

    str = str!.toLowerCase();
    
    return str.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
  }

  /**
   * 转换为帕斯卡命名（首字母大写的驼峰）
   * 
   * @param str 下划线分隔的字符串
   * @returns 帕斯卡命名字符串
   */
  public static toPascalCase(str: string | null | undefined): string {
    if (this.isEmpty(str)) {
      return this.EMPTY;
    }

    const camel = this.toCamelCase(str);
    return camel.charAt(0).toUpperCase() + camel.slice(1);
  }

  /**
   * 转换为下划线命名
   * 
   * @param str 驼峰命名的字符串
   * @returns 下划线分隔的字符串
   */
  public static toUnderscoreCase(str: string | null | undefined): string {
    if (str === null || str === undefined) {
      return this.EMPTY;
    }

    return str
      .replace(/([a-z\d])([A-Z])/g, '$1_$2')
      .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1_$2')
      .toLowerCase();
  }

  /**
   * 判断字符串是否以指定前缀开始
   * 
   * @param str 源字符串
   * @param prefix 前缀
   * @returns 是否以指定前缀开始
   */
  public static startsWith(str: string | null | undefined, prefix: string): boolean {
    if (str === null || str === undefined || prefix === null) {
      return false;
    }
    return str.startsWith(prefix);
  }

  /**
   * 判断字符串是否以指定后缀结束
   * 
   * @param str 源字符串
   * @param suffix 后缀
   * @returns 是否以指定后缀结束
   */
  public static endsWith(str: string | null | undefined, suffix: string): boolean {
    if (str === null || str === undefined || suffix === null) {
      return false;
    }
    return str.endsWith(suffix);
  }

  /**
   * 判断字符串是否包含子串
   * 
   * @param str 源字符串
   * @param searchStr 查找的子串
   * @returns 是否包含子串
   */
  public static contains(str: string | null | undefined, searchStr: string): boolean {
    if (str === null || str === undefined || searchStr === null) {
      return false;
    }
    return str.includes(searchStr);
  }

  /**
   * 判断字符串是否包含子串（忽略大小写）
   * 
   * @param str 源字符串
   * @param searchStr 查找的子串
   * @returns 是否包含子串
   */
  public static containsIgnoreCase(str: string | null | undefined, searchStr: string): boolean {
    if (str === null || str === undefined || searchStr === null) {
      return false;
    }
    return str.toLowerCase().includes(searchStr.toLowerCase());
  }

  /**
   * 将字符串重复指定次数
   * 
   * @param str 源字符串
   * @param times 重复次数
   * @returns 重复后的字符串
   */
  public static repeat(str: string | null | undefined, times: number): string {
    if (this.isEmpty(str) || times <= 0) {
      return this.EMPTY;
    }
    return str!.repeat(times);
  }

  /**
   * 填充字符串到指定长度
   * 
   * @param str 源字符串
   * @param size 目标长度
   * @param padChar 填充字符
   * @returns 填充后的字符串
   */
  public static padLeft(str: string | null | undefined, size: number, padChar: string = ' '): string {
    if (str === null || str === undefined) {
      str = this.EMPTY;
    }
    
    if (str.length >= size) {
      return str;
    }
    
    const padding = this.repeat(padChar, size - str.length);
    return padding + str;
  }

  /**
   * 将字符串拆分为数组
   * 
   * @param str 源字符串
   * @param separator 分隔符
   * @returns 字符串数组
   */
  public static split(str: string | null | undefined, separator: string): string[] {
    if (this.isEmpty(str)) {
      return [];
    }
    return str!.split(separator);
  }

  /**
   * 将字符串拆分为数组并过滤空白项
   * 
   * @param str 源字符串
   * @param separator 分隔符
   * @returns 字符串数组
   */
  public static splitWithFilter(str: string | null | undefined, separator: string): string[] {
    const result = this.split(str, separator);
    return result.filter(item => this.isNotBlank(item)).map(item => item.trim());
  }

  /**
   * 将字符串拆分为Set集合
   * 
   * @param str 源字符串
   * @param separator 分隔符
   * @returns 字符串Set
   */
  public static splitToSet(str: string | null | undefined, separator: string): Set<string> {
    const array = this.splitWithFilter(str, separator);
    return new Set(array);
  }

  /**
   * URL编码
   * 
   * @param str 源字符串
   * @returns 编码后的字符串
   */
  public static urlEncode(str: string | null | undefined): string {
    if (this.isEmpty(str)) {
      return this.EMPTY;
    }
    return encodeURIComponent(str!);
  }

  /**
   * URL解码
   * 
   * @param str 编码的字符串
   * @returns 解码后的字符串
   */
  public static urlDecode(str: string | null | undefined): string {
    if (this.isEmpty(str)) {
      return this.EMPTY;
    }
    return decodeURIComponent(str!);
  }
} 